package com.it.smn.divide.loadbalance;

import com.it.smn.divide.datasource.AbstractDataSourceWrapper;

import javax.sql.DataSource;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;

/**
 * @author sunmannian
 * @ClassName com.it.smn.divide.loadbalance.RandomLoadBalance
 * @Date 2020/8/27 18:36
 * @Version V1.0
 * Description  <随机权重负载均衡算法>
 * Copyright (C) 北京中电兴发科技有限公司  since 2020/8/27
 */
public class RandomLoadBalance implements LoadBalanceStrategy {

    @Override
    public DataSource select(List<DataSource> dataSources) {
        return doSelect(dataSources);
    }

    private DataSource doSelect(List<DataSource> dataSources) {
        // Number of dataSources
        int length = dataSources.size();
        if (length == ONLY_ONE) {
            return dataSources.get(0);
        }
        // Every invoker has the same weight?
        boolean sameWeight = true;
        // the weight of every invokers
        int[] weights = new int[length];
        // the first invoker's weight
        int firstWeight = ((AbstractDataSourceWrapper) dataSources.get(0)).getWeight();
        weights[0] = firstWeight;
        // The sum of weights
        int totalWeight = firstWeight;
        for (int i = 1; i < length; i++) {
            int weight = ((AbstractDataSourceWrapper) dataSources.get(i)).getWeight();
            // save for later use
            weights[i] = weight;
            // Sum
            totalWeight += weight;
            if (sameWeight && weight != firstWeight) {
                sameWeight = false;
            }
        }
        if (totalWeight > 0 && !sameWeight) {
            // If (not every invoker has the same weight & at least one invoker's weight>0), select randomly based on totalWeight.
            int offset = ThreadLocalRandom.current().nextInt(totalWeight);
            // Return a invoker based on the random value.
            for (int i = 0; i < length; i++) {
                offset -= weights[i];
                if (offset < 0) {
                    return dataSources.get(i);
                }
            }
        }
        // If all invokers have the same weight value or totalWeight=0, return evenly.
        return dataSources.get(ThreadLocalRandom.current().nextInt(length));
    }


}
